package cn.com.myspring.framework.aop.support;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cn.com.myspring.framework.aop.aspect.MYAfterReturningAdviceInterceptor;
import cn.com.myspring.framework.aop.aspect.MYAfterThrowingAdviceInterceptor;
import cn.com.myspring.framework.aop.aspect.MYMethodBeforeAdviceInterceptor;
import cn.com.myspring.framework.aop.config.MYAopConfig;

/**
 * @ClassName MYAdvisedSupport
 * @Description AOP通知支持
 * @Date 2019年7月29日 上午9:43:57
 * @version 1.0.0
 */
public class MYAdvisedSupport {

    private Class<?> targetClass;

    private Object target;

    private MYAopConfig config;

    private Pattern pointCutClassPattern;

    /**
     * @Field @methodCache : 方法缓存,key:方法 value:Advivce执行链
     */
    private transient Map<Method, List<Object>> methodCache;

    public MYAdvisedSupport(MYAopConfig config) {
        this.config = config;
    }

    public Class<?> getTargetClass() {
        return this.targetClass;
    }

    public Object getTarget() {
        return this.target;
    }

    /**
     * @Description 根据目标方法获取Advivce执行链
     * @param method
     * @param targetClass
     * @return
     * @throws Exception
     */
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass)
            throws Exception {
        List<Object> cached = methodCache.get(method);
        if (cached == null) {
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());

            cached = methodCache.get(m);

            // 底层逻辑，对代理方法进行一个兼容处理
            this.methodCache.put(m, cached);
        }

        return cached;
    }

    /**
     * @Description 设置目标对象类型,以及解析
     * @param targetClass
     */
    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    private void parse() {
        // 配置文件 public .* cn.com.myspring.demo.service..*(.*)
        String pointCut = config.getPointCut().replaceAll("\\.", "\\\\.").replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(").replaceAll("\\)", "\\\\)");
        // 匹配方法 public .* cn\.com\.myspring\.demo\.service\..*\(.*\)
        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        // 截断 public .* cn\.com\.myspring\.demo\.service
        String pointCutForClassRegexSub =
                "class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1);
        // 匹配类 class cn\.com\.myspring\.demo\.service
        pointCutClassPattern = Pattern.compile(pointCutForClassRegexSub);

        try {

            methodCache = new HashMap<Method, List<Object>>();

            Class aspectClass = Class.forName(this.config.getAspectClass());
            // 切面方法 key:方法名  value:方法
            Map<String, Method> aspectMethods = new HashMap<String, Method>();
            for (Method m : aspectClass.getMethods()) {
                aspectMethods.put(m.getName(), m);
            }

            for (Method m : this.targetClass.getMethods()) {
                String methodString = m.toString();
                if (methodString.contains("throws")) {
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
                }
                
                // 匹配方法
                Pattern pattern = Pattern.compile(pointCut);
                Matcher matcher = pattern.matcher(methodString);
                if (matcher.matches()) {
                    // 执行器链
                    List<Object> advices = new LinkedList<Object>();
                    // 把每一个方法包装成 MethodIterceptor
                    // before
                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                        // 创建一个Advivce
                        advices.add(new MYMethodBeforeAdviceInterceptor(aspectMethods.get(config.getAspectBefore()),
                                aspectClass.newInstance()));
                    }
                    // after
                    if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                        // 创建一个Advivce
                        advices.add(new MYAfterReturningAdviceInterceptor(aspectMethods.get(config.getAspectAfter()),
                                aspectClass.newInstance()));
                    }
                    // afterThrowing
                    if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
                        // 创建一个Advivce
                        MYAfterThrowingAdviceInterceptor throwingAdvice = new MYAfterThrowingAdviceInterceptor(
                                aspectMethods.get(config.getAspectAfterThrow()), aspectClass.newInstance());
                        throwingAdvice.setThrowName(config.getAspectAfterThrowingName());
                        advices.add(throwingAdvice);
                    }
                    methodCache.put(m, advices);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void setTarget(Object target) {
        this.target = target;
    }

    /**
     * @Description 判断目标对象与切点是否匹配
     * @return
     */
    public boolean pointCutMatch() {
        return pointCutClassPattern.matcher(this.targetClass.toString()).find();
    }

}
